package com.demo;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ExecutionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.junit.Before;
import org.junit.Test;
import org.springframework.util.StringUtils;

public class ActivitiTest04{
	
	RepositoryService repositoryService = null;
	
	RuntimeService runtimeService = null;
	
	TaskService taskService = null;
	
	HistoryService historyService = null;
	
	@Before
	public void setUp() throws Exception{
		// 创建流程引擎对象
		ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
		// 获取repositoryService
		repositoryService = processEngine.getRepositoryService();
		runtimeService = processEngine.getRuntimeService();
		taskService = processEngine.getTaskService();
		historyService = processEngine.getHistoryService();
	}
	
	/*
	 * 部署流程
	 */
	@Test
	public void deployProcess1(){
		// 创建部署对象
		DeploymentBuilder deploymentBuilder =repositoryService.createDeployment();
		// 加载流程的配置文件和图片
		deploymentBuilder.addClasspathResource("activiti_test01.bpmn")
							.addClasspathResource("activiti_test01.png");
		// 部署			 
		deploymentBuilder.deploy();
	}
	
	/*
	 * 部署流程
	 */
	@Test
	public void deployProcessAddNameAndCategory(){
		// 创建部署对象
		DeploymentBuilder deploymentBuilder =repositoryService.createDeployment();
		// 加载流程的配置文件和图片
		deploymentBuilder.name("请假流程").category("请假类").addClasspathResource("activiti_test01.bpmn")
							.addClasspathResource("activiti_test01.png");
		// 部署			 
		deploymentBuilder.deploy();
	}
	
	/*
	 * 通过zip压缩文件部署流程,zip里包含.bpmn和.png,只是进行压缩
	 */
	@Test
	public void deployProcessByZip(){
		// 创建部署对象
		DeploymentBuilder deploymentBuilder =repositoryService.createDeployment();
		// 加载流程的配置文件和图片
		InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("activitiFile.zip");
		ZipInputStream zipInput = new ZipInputStream(inputStream);
		deploymentBuilder.addZipInputStream(zipInput)
			.name("请假流程")
			.category("请假类");
		
		// 部署			 
		deploymentBuilder.deploy();
	}
	
	/*
	 * 删除流程部署
	 */
	@Test
	public void deleteActivitiDeploy(){
		// act_re_deployment流程部署表的id
		String deploymentId = "801";
		// 如果流程已经启动，会报错
		repositoryService.deleteDeployment(deploymentId);
		
		String deploymentId1 = "701";
		//第二个参数是true级联删除，流程启动了有数据，也能删除，false,有数据会报错
		repositoryService.deleteDeployment(deploymentId1, true);
	}
	
	/*
	 * 启动流程
	 */
	@Test
	public void startProcess(){
		/* 启动流程，根据流程定义的key，如果相同的流程定义有多个，那么启动version最高的那个。
		 * 如果想启用其他版本，根据流程定义的id（act_re_procdef表的id）
		 * 如果key相同，做第二次部署会在原有的key的流程上做版本的累加
		 */
		ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveProcess");
		String processInstanceId = processInstance.getProcessInstanceId();
		String activityId = processInstance.getActivityId();
		String definitionId = processInstance.getProcessDefinitionId();
		System.out.println("流程实例id:" + processInstanceId);
		System.out.println("正在活动的节点:" + activityId);
		System.out.println("流程定义id:" + definitionId);	
	}
	
	/*
	 *查询流程实例对象 
	 */
	@Test
	public void queryProcessInstance(){
		String processDefinitionKey = "leaveProcess";
		// 创建流程实例查询对象
		ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
		
		List<ProcessInstance> list = processInstanceQuery.processDefinitionKey(processDefinitionKey)
				.orderByProcessDefinitionKey()
				.desc()
				.list();
		for (ProcessInstance processInstance : list) {
			System.out.println("流程实例id:" + processInstance.getProcessInstanceId());
			System.out.println("正在活动的节点:" + processInstance.getActivityId());
			System.out.println("流程定义id:" + processInstance.getProcessDefinitionId());
		}
		
		// 如果能确定流程定义的key是唯一的，则调用用singleResult()。不唯一的话会报错！！！！
		ProcessInstance processInstance1 = processInstanceQuery.processDefinitionKey(processDefinitionKey)
				.singleResult();
		
		System.out.println("流程实例id:" + processInstance1.getProcessInstanceId());
		System.out.println("正在活动的节点:" + processInstance1.getActivityId());
		System.out.println("流程定义id:" + processInstance1.getProcessDefinitionId());
	}
	
	/*
	 * 查询流程定义1
	 */
	@Test
	public void queryProcessDefinition(){
		String processDefinitionId = "";
		ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
		ProcessDefinition processDefinition = processDefinitionQuery.processDefinitionId(processDefinitionId).singleResult();
		String id = processDefinition.getId();
		String key = processDefinition.getKey();
		String name = processDefinition.getName();
		System.out.println(id);
		System.out.println(key);
		System.out.println(name);
	}
	
	/*
	 * 查询流程定义2
	 */
	@Test
	public void queryProcessDefinition1(){
		String processDefinitionKey = "";
		ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
		
		List<ProcessDefinition> processDefinitionList = processDefinitionQuery.processDefinitionKey(processDefinitionKey)
				.orderByProcessDefinitionKey()
				.desc().list();
		for (ProcessDefinition processDefinition : processDefinitionList) {
			String id = processDefinition.getId();
			String key = processDefinition.getKey();
			String name = processDefinition.getName();
			System.out.println(id);
			System.out.println(key);
			System.out.println(name);
		}		
	}
	
	/*
	 * 查询正在执行的流程对象,可以看作是指针，流到哪个节点，该对象就指向哪个节点（包含节点信息）
	 */
	@Test
	public void queryExecution(){
		String processDefinitionKey = "leave_process";
		ExecutionQuery executionQuery = runtimeService.createExecutionQuery();
		
		List<Execution> ExecutionList = executionQuery.processDefinitionKey(processDefinitionKey)
			.list();
		for (Execution execution : ExecutionList) {
			String activityId = execution.getActivityId();
			String id = execution.getId();
			String processInstanceId = execution.getProcessInstanceId();
			System.out.println("正在活动的节点：" + activityId);
			System.out.println("正在执行的流程对象id：" + id);
			System.out.println("所属流程实例id：" + processInstanceId);
		}
	}
	
	/*
	 *根据办理人来查询他全部任务
	 */
	@Test
	public void queryTask(){
		String processDefinitionKey = "";
		TaskQuery taskQuery = taskService.createTaskQuery();
		
		List<Task> taskList = taskQuery.processDefinitionKey(processDefinitionKey)
			.taskAssignee("")
			.orderByTaskCreateTime()
			.desc()
			.list();
		for (Task task : taskList) {
			String id = task.getId();
			String name = task.getName();
			String processDefinitionId = task.getProcessDefinitionId();
			String taskDefinitionKey = task.getTaskDefinitionKey();
			System.out.println(id);
			System.out.println(name);
			System.out.println(processDefinitionId);
			System.out.println(taskDefinitionKey);
		}
	}
	
	
	
	/*
	 * 根据办理人办理任务
	 */
	@Test
	public void completeTaskByAssignee(){
		String processDefinitionKey = "";
		String assignee = "";
		List<Task> taskList = taskService.createTaskQuery().processDefinitionKey(processDefinitionKey)
			.taskAssignee(assignee)
			.orderByTaskCreateTime()
			.asc()
			.list();
		
		Task task = taskList.get(0);
		taskService.complete(task.getId());
	}
	
	/*
	 *查询流程实例状态，即查询流到哪个节点 
	 */
	@Test
	public void queryProcessInstanceState(){
		String processInstanceId = "";
		ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
				.processInstanceId(processInstanceId).singleResult();
		if(processInstance == null){
			System.out.println("当前流程已经完成");
		}else{
			System.out.println("当前流程所处的节点:" + processInstance.getActivityId());
			System.out.println("流程实例的id:" + processInstance.getProcessInstanceId());
			System.out.println("流程实例的id:" + processInstance.getId());
		}
	}
	
	/*
	 *流程实例上的变量 
	 */
	@Test
	public void startProcessInstanceWithVariables(){
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("请假天数", 3);
		map.put("请假原因", "旅游去");
		map.put("请假开始时间", new Date());
		// 在流程实例上添加变量，影响的范围：当前流程实例下的所有任务都能获取到流程变量，正在执行的流程对象也能获取到
		// 当流程实例结束时流程变量消失，正在执行的对象消失，流程变量历史表保留变量
		// 影响到的表：select * from act_ru_variable; --存储变量的表
		//         select * from act_hi_varinst;  --流程变量历史表
		runtimeService.startProcessInstanceByKey("leave_process", map);
	}
	
	/*
	 * 获取流程变量
	 */
	@Test
	public void getVariables(){
		String assignee = "employee";
		List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("leave_process")
				.taskAssignee(assignee)
				.orderByTaskCreateTime()
				.desc()
				.list();
		Task task = taskList.get(0);
		// 1.在当前任务中获取流程变量
		Map<String, Object> map = taskService.getVariables(task.getId());
		System.out.println(map);
		// 2.从正在执行的流程对象中获取流程变量
		List<Execution> executionList = runtimeService.createExecutionQuery().processDefinitionKey("leave_process")
			.orderByProcessDefinitionId()
			.asc()
			.list();
		Execution execution = executionList.get(0);
		Map<String, Object> map1 = runtimeService.getVariablesLocal(execution.getId());
		System.out.println(map1);
	}
	
	/*
	 * 在任务中设置流程变量,流程的变量存储在正在执行的流程对象上，当前任务后面的所有任务都能拿到流程变量
	 */
	@Test
	public void setVariablesAtTask(){
		String assignee = "employee";
		Task task = taskService.createTaskQuery().processDefinitionKey("leave_process").taskAssignee(assignee).singleResult();
		taskService.setVariable(task.getId(), "请假天数", 3);
		taskService.setVariable(task.getId(), "请假原因", "结婚去");
		taskService.setVariable(task.getId(), "请假开始时间", new Date());
	}
	
	/*
	 * 在任务中获取变量
	 */
	@Test
	public void getVariablesAtTask(){
		String assignee = "employee";
		Task task = taskService.createTaskQuery().processDefinitionKey("leave_process").taskAssignee(assignee).singleResult();
		System.out.println(taskService.getVariable(task.getId(), "请假天数"));
		System.out.println(taskService.getVariable(task.getId(), "请假原因"));
		System.out.println(taskService.getVariable(task.getId(), "请假开始时间"));
	}
	
	
	/*
	 * 完成任务也能设置流程变量
	 */
	@Test
	public void setVariablesByCompleteTask(){
		String assignee = "employee";
		Task task = taskService.createTaskQuery().processDefinitionKey("leave_process").taskAssignee(assignee).singleResult();

		Map<String, Object> map = new HashMap<String, Object>();
		map.put("请假天数", 3);
		map.put("请假原因", "结婚去");
		map.put("请假开始时间", new Date());
		taskService.complete(task.getId(), map);
	}
	
	/*获取某一任务的所有入口*/
	public List<String> getInComingTransNames(String taskId) {
		List<String> transNames = new ArrayList<String>();
		// 1.获取流程定义
		Task task = this.taskService.createTaskQuery().taskId(taskId).singleResult();
		ProcessDefinitionEntity pd = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(task.getProcessDefinitionId());
		// 2.获取流程实例
		ProcessInstance pi =runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult(); 
		// 3.通过流程实例查找当前活动的ID
		String activitiId = pi.getActivityId();
		// 4.通过活动的ID在流程定义中找到对应的活动对象
		ActivityImpl activity = pd.findActivity(activitiId);
		// 5.通过活动对象找当前活动的所有入口
		 List<PvmTransition> transitions =  activity.getIncomingTransitions();
		// 6.提取所有入口的名称，封装成集合
		 for (PvmTransition trans : transitions) {
		     String transName = (String) trans.getProperty("name");
		     if(transName != null && !"".equals(transName)){
		    	 transNames.add(transName);
		    	 System.out.println("入口：" + transName);
		     }
		}
/*		 if(transNames.size()==0){
		     transNames.add("提交");//默认
		 }*/
		return transNames;
	}
	
	/*根据taskId获取任务的所有入口*/
	public List<String> getOutGoingTransNames(String taskId) {
		List<String> transNames = new ArrayList<String>();
		// 1.获取流程定义
		Task task = this.taskService.createTaskQuery().taskId(taskId).singleResult();
		ProcessDefinitionEntity pd = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(task.getProcessDefinitionId());
		// 2.获取流程实例
		ProcessInstance pi =runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult(); 
		// 3.通过流程实例查找当前活动的ID
		String activitiId = pi.getActivityId();
		// 4.通过活动的ID在流程定义中找到对应的活动对象
		ActivityImpl activity = pd.findActivity(activitiId);
		// 5.通过活动对象找当前活动的所有出口
		 List<PvmTransition> transitions =  activity.getOutgoingTransitions();
		// 6.提取所有出口的名称，封装成集合
		 for (PvmTransition trans : transitions) {
		     String transName = (String) trans.getProperty("name");
		     if(transName != null && !"".equals(transName)){
		    	 transNames.add(transName);
		    	 System.out.println("出口：" + transName);
		     }
		}
/*		 if(transNames.size()==0){
		     transNames.add("提交");//默认
		 }*/
		return transNames;
	}
	
	/*根据taskId获取上一节点(任务)的所有出口*/
	public List<String> getPreOutGoingTransNames(String currentTaskId){
		Task task = taskService.createTaskQuery().taskId(currentTaskId).singleResult();		
		List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
			.processInstanceId(task.getProcessInstanceId())
			.orderByHistoricActivityInstanceStartTime()
			.finished()
			.desc()
			.list();
		HistoricActivityInstance historicActivityInstance = historicActivityInstanceList.get(0);
		String preTaskId = historicActivityInstance.getTaskId();
		List<String> strList = new ArrayList<String>();
		strList = getOutGoingTransNames(preTaskId);
		return strList;
	}
	
	/*根据某个任务的taskId获取任务的来路（入口，只有一个）*/
	public String getInComingTransName(String currentTaskId){
		String inComing = null;
		List<String> preTaskOutGoingList =  getPreOutGoingTransNames(currentTaskId);
		List<String> currentTaskIncomingList = getInComingTransNames(currentTaskId);
		for(String preOutGoing: preTaskOutGoingList){
			for(String currentInComing: currentTaskIncomingList){
				if(preOutGoing.equals(currentInComing)){
					inComing = preOutGoing;
				}
			}
		}
		return inComing;
	}
}